This file endeavors to visualize the effectiveness of social distancing measures against the COVID-19 pandemic. We perform k-means cluster analysis to identify classifications of how strict social distancing government mandates are in each of the 50 U.S. States. We then use these groupings to visualize coronavirus cases and deaths in each state.
library(tidyverse)
library(grid)
library(gridExtra)
library(ggplot2)
library(plotly)
library(ggpubr)
library(usmap)
library(fiftystater)
Social Distancing Data Sources
To begin our analysis of the effectiveness of social distancing, we look at a dataset which reports information about the measures mandated in each of the 50 U.S. States. KFF has a dataset with extensive records detailing this information. Let’s begin by loading in this dataset. It contains a lot of columns with a lot of information, but for our purposes we’re going to focus on Location, Stay at Home Order, Large Gatherings Ban, and Restaurant Limits. These 3 categories were selected over the others based on the magnitude of available data, variance between states, and relevance to enforcing social distancing. Please note that while this dataset was grabbed from KFF as linked, the csv was mildly massaged before loading into R (removing metadata lines, footnotes, ensuring consistent number of columns in each row). This data was downloaded on and accurate as of May 12, 2020.
social_distancing <- as.data.frame(read_csv('./social_distancing.csv'))
Parsed with column specification:
cols(
Location = [31mcol_character()[39m,
`State Is Easing Social Distancing Measures` = [31mcol_character()[39m,
`Stay at Home Order` = [31mcol_character()[39m,
`Mandatory Quarantine for Travelers` = [31mcol_character()[39m,
`Non-Essential Business Closures` = [31mcol_character()[39m,
`Large Gatherings Ban` = [31mcol_character()[39m,
`School Closures` = [31mcol_character()[39m,
`Restaurant Limits` = [31mcol_character()[39m,
`Primary Election Postponement` = [31mcol_character()[39m,
`Emergency Declaration` = [31mcol_character()[39m
)
row.names(social_distancing) <- social_distancing$Location
social_distancing <- social_distancing %>% select('Location','Stay at Home Order','Large Gatherings Ban','Restaurant Limits')
social_distancing[1:10,]
Transforming Categorical Variables
Our goal is to use k-means clustering analysis to identify classifications of how strict each state is with their social distancing policies. However, our data in it’s current state contains only categorical variables, and k-means clustering requires the use of numeric variables. Luckily, the categories we have are inherently ordinal, so let’s define a mapping for each variable to become “numerical”. We will take the categories for each variable, and assign them an integer value between 0 and 10, loosely defined as 0 being the “least strict” and 10 being the “most strict”. Keeping all 3 predictor variables on a similar scale allows k-means to effectively analyze clusters without allowing any single variable to overrun the analysis.
As a note, this type of transformation is highly subjective: more of an art form than a science. A different analyst may have different ideas about how to assign weight to the levels of each factor. Regardless, perhaps the most important principles to follow during this process is to keep each variable on scales of similar magnitude.
Transforming “Stay at Home Order”
unique(social_distancing[,'Stay at Home Order'])
[1] "Lifted" "Statewide" "-"
[4] "Rolled Back to High Risk Groups" "High-Risk Groups"
We can see here that there are 4 categories of responses to the “Stay at Home” variable: Lifted, High-Risk Groups, Rolled Back to High Risk Groups, and Statewide. There is also a “-” response, indicating some type of missing value. Which states only record “-” for Stay at Home Order?
filter(social_distancing,`Stay at Home Order`=='-')
Some outside research indicates that the value is missing because these states actually have no stay at home order! This must be the meaning of “-”. Turns out then, we actually have 5 categories for this variable.
This is where it becomes subjective, but we will do our best to create a fair mapping between this somewhat-ordinal data and a numerical 0-10 scale. Below we assign each category of “Stay at Home Order” to a numerical value, and create a new column to record these.
# Rolled Back to High Risk Groups is classified as more strict than High-Risk groups
# because states in this classification at one point had statewide stay at home orders
social_distancing$stay_home <- ifelse(social_distancing$`Stay at Home Order`=='-', 0,
ifelse(social_distancing$`Stay at Home Order`=='Lifted', 3,
ifelse(social_distancing$`Stay at Home Order`=='High-Risk Groups', 5,
ifelse(social_distancing$`Stay at Home Order`=='Rolled Back to High Risk Groups', 8,
ifelse(social_distancing$`Stay at Home Order`=='Statewide', 10,
NA)))))
social_distancing
Transforming “Large Gatherings Ban”
We will follow similar steps to transform the “Large Gatherings Ban” predictor variable.
unique(social_distancing[,'Large Gatherings Ban'])
[1] "Lifted" "Expanded to 50+ People Prohibited" ">10 People Prohibited"
[4] "All Gatherings Prohibited" "Expanded to >10 People Prohibited" "Other"
[7] "Expanded to >25 People Prohibited" "-" "Expanded to 20+ People Prohibited"
[10] "Expanded to 25+ People Prohibited"
Most of these categories seem to make sense and are fairly ordinal. However, two of the categories are unclear in their meaning: ‘-’ and ‘Other’. Let’s look at which states fall into these categories and see if we can learn more information.
filter(social_distancing, `Large Gatherings Ban`=='-')
Doing some outside research, it appears Minnesota has banned large gatherings for high school graduation ceremonies, but has made no general mandates for large gatherings outside of that, only suggestions. It appears North Dakota has not made a mandate concerning large gatherings.
filter(social_distancing, `Large Gatherings Ban`=='Other')
Research indicates that Connecticut is banning gatherings with >5 people, Florida prohibits gatherings of 10+ on beaches only, and Rhode Island bans gatherings of 10+.
Based on these findings for MN, ND, CT, FL, and RI, we define new categories of “None”, “Special types of gatherings prohibited”, and “>5 People Prohibited”. We will then re-classify the “Large Gatherings Ban” attribute in ways that make sense for these 5 states.
social_distancing['Minnesota', 'Large Gatherings Ban'] <- 'Special types of gatherings prohibited'
social_distancing['North Dakota', 'Large Gatherings Ban'] <- 'None'
social_distancing['Connecticut', 'Large Gatherings Ban'] <- '>5 People Prohibited'
social_distancing['Florida', 'Large Gatherings Ban'] <- 'Special types of gatherings prohibited'
social_distancing['Rhode Island', 'Large Gatherings Ban'] <- '>10 People Prohibited'
Let’s look at the unique levels of this factor one more time to make sure nothing went awry.
unique(social_distancing[,'Large Gatherings Ban'])
[1] "Lifted" "Expanded to 50+ People Prohibited" ">10 People Prohibited"
[4] "All Gatherings Prohibited" "Expanded to >10 People Prohibited" ">5 People Prohibited"
[7] "Special types of gatherings prohibited" "Expanded to >25 People Prohibited" "None"
[10] "Expanded to 20+ People Prohibited" "Expanded to 25+ People Prohibited"
Great! Now let’s go ahead and assign numerical scores to each of these levels, creating a new column in our dataframe to store those values. Notice that ‘Expanded to >25 People Prohibited’ and ‘Expanded to 25+ People Prohibited’ are two different labels, but we will treat these as equally strict categories.
social_distancing$large_gatherings <- ifelse(social_distancing$`Large Gatherings Ban`=='None', 0,
ifelse(social_distancing$`Large Gatherings Ban`=='Lifted', 3,
ifelse(social_distancing$`Large Gatherings Ban`=='Special types of gatherings prohibited',3,
ifelse(social_distancing$`Large Gatherings Ban`=='Expanded to 50+ People Prohibited', 4,
ifelse(social_distancing$`Large Gatherings Ban`=='Expanded to 25+ People Prohibited', 5,
ifelse(social_distancing$`Large Gatherings Ban`=='Expanded to >25 People Prohibited', 5,
ifelse(social_distancing$`Large Gatherings Ban`=='Expanded to 20+ People Prohibited', 6,
ifelse(social_distancing$`Large Gatherings Ban`=='Expanded to >10 People Prohibited', 7,
ifelse(social_distancing$`Large Gatherings Ban`=='>10 People Prohibited', 8,
ifelse(social_distancing$`Large Gatherings Ban`=='>5 People Prohibited', 9,
ifelse(social_distancing$`Large Gatherings Ban`=='All Gatherings Prohibited', 10,
NA)))))))))))
social_distancing
K-Means Clustering
Now that our dataset is in a helpfully numeric form, we can perform k-means clustering analysis. For the sake of convenience, let’s define a subset of the data that only contains the relevant columns.
clustering_data <- social_distancing[,c("stay_home", "large_gatherings", "restaurants")]
clustering_data
Now let’s try to identify the optimal number of clusters. We’ll check from 1 to 15 clusters
wss <- numeric(15)
for (k in 1:15) wss[k] <- sum(kmeans(clustering_data, centers = k, nstart = 30)$withinss)
plot(1:15, wss, type = "b", xlab = "Number of Clusters", ylab = "Within Sum of Squares")

km <- kmeans(clustering_data, 2, nstart = 25)
km
K-means clustering with 2 clusters of sizes 21, 30
Cluster means:
stay_home large_gatherings restaurants
1 2.238095 5.714286 6.285714
2 9.866667 8.633333 9.433333
Clustering vector:
Alabama Alaska Arizona Arkansas California Colorado
1 1 2 1 2 2
Connecticut Delaware District of Columbia Florida Georgia Hawaii
2 2 2 1 2 2
Idaho Illinois Indiana Iowa Kansas Kentucky
1 2 1 1 1 2
Louisiana Maine Maryland Massachusetts Michigan Minnesota
2 2 2 2 2 2
Mississippi Missouri Montana Nebraska Nevada New Hampshire
1 1 1 1 2 2
New Jersey New Mexico New York North Carolina North Dakota Ohio
2 2 2 2 1 2
Oklahoma Oregon Pennsylvania Rhode Island South Carolina South Dakota
1 2 2 2 1 1
Tennessee Texas Utah Vermont Virginia Washington
1 1 1 2 2 2
West Virginia Wisconsin Wyoming
1 2 1
Within cluster sum of squares by cluster:
[1] 300.381 151.800
(between_SS / total_SS = 67.7 %)
Available components:
[1] "cluster" "centers" "totss" "withinss" "tot.withinss" "betweenss" "size" "iter"
[9] "ifault"
Let’s visualize our data. Since all points are likely to be on top of each other, we plot with some jitter.
df <- as.data.frame(clustering_data)
df$cluster <- factor(km$cluster)
centers <- as.data.frame(km$centers)
shlg <- ggplot(data = df, aes(x = stay_home, y = large_gatherings, color = cluster)) +
geom_jitter() + theme(legend.position = "right") +
geom_point(data = centers, aes(x = stay_home, y = large_gatherings, color = as.factor(c(1,2))),
size = 10, alpha = .3, show.legend = FALSE)
shr <- ggplot(data = df, aes(x = stay_home, y = restaurants, color = cluster)) +
geom_jitter() + theme(legend.position = "right") +
geom_point(data = centers, aes(x = stay_home, y = restaurants, color = as.factor(c(1,2))),
size = 10, alpha = .3, show.legend = FALSE)
lgr <- ggplot(data = df, aes(y = large_gatherings, x = restaurants, color = cluster)) +
geom_jitter() + theme(legend.position = "right") +
geom_point(data = centers, aes(y = large_gatherings, x = restaurants, color = as.factor(c(1,2))),
size = 10, alpha = .3, show.legend = FALSE)
grid.arrange(shlg + theme(legend.position = "none"),
shr + theme(legend.position = "none"),
lgr + theme(legend.position = "none"),
top = "Social Distancing Clusters", ncol = 1)

This is very raw data (even graphed in it’s “numerical” format despite being representative of categorical data), so we definitely won’t include them in the final presentation. Still, they are illustrative in showing how well the clustering algorithm works, and if the clusters really are distinct. It appears that stay at home orders were the biggest dividing factor betwteen states, but restaurants and large gatherings maintained some predictive power as well. Let’s add one more column to our dataset to differentiate between “More Strict” and “Less Strict” for “Social Distancing Mandates”.
if(km$centers[1] < km$centers[2]){
cluster_titles <- c('Less Strict', 'More Strict')
} else {
cluster_titles <- c('More Strict', 'Less Strict')
}
identify_cluster <- function(num){
return(cluster_titles[num])
}
clustering_data$`Social Distancing Mandates` <- identify_cluster(km$cluster)
clustering_data
Awesome! Now we have a decent classification of how strict each of the U.S. States have been with their statewide social distancing mandates, and we are ready to take this data and compare it to how well each state is doing in terms of cases and, especially, deaths. First, let’s quickly visualize this data to see where each state ended up.
plot_sd_data <- clustering_data %>%
rownames_to_column(var = 'state') %>%
rename(social_distancing = `Social Distancing Mandates`) %>%
right_join(states, by = c('state' = 'state_name'))
sd_map <- ggplot(plot_sd_data, aes(map_id = tolower(state))) +
geom_map(aes(fill = social_distancing), color = 'white', map = fifty_states) +
expand_limits(x = fifty_states$long, y = fifty_states$lat) +
coord_map() +
labs(x = "", y = "", fill = 'Social Distancing') +
theme(legend.position = "bottom",
panel.background = element_blank())
ggsave('SocialDistancingMap.png')
Saving 7 x 7 in image
sd_map

states
Cases and Deaths Data Source
To obtain information regarding deaths and cases, we will use the Johns Hopkins data sources. Since our social distancing data was current as of May 12, we will use data from that same day for the sake of consistency.
johns_hopkins_data <- as.data.frame(read_csv('https://raw.githubusercontent.com/CSSEGISandData/COVID-19/master/csse_covid_19_data/csse_covid_19_daily_reports/05-12-2020.csv'))
Parsed with column specification:
cols(
FIPS = [32mcol_double()[39m,
Admin2 = [31mcol_character()[39m,
Province_State = [31mcol_character()[39m,
Country_Region = [31mcol_character()[39m,
Last_Update = [34mcol_datetime(format = "")[39m,
Lat = [32mcol_double()[39m,
Long_ = [32mcol_double()[39m,
Confirmed = [32mcol_double()[39m,
Deaths = [32mcol_double()[39m,
Recovered = [32mcol_double()[39m,
Active = [32mcol_double()[39m,
Combined_Key = [31mcol_character()[39m
)
states_data <- johns_hopkins_data %>%
filter(Country_Region == 'US') %>%
select(Province_State, Confirmed, Deaths) %>%
group_by(Province_State) %>%
summarise(total_confirmed = sum(Confirmed),
total_deaths = sum(Deaths)) %>%
rename(state = Province_State)
states_data
This data is interesting as is, but in order to be as honestly representative as possible of reality, we really want this data to be somehow relative to total population size in each state. Let’s import the most recent data from the US Census Bureau about the population in each state and merge it with our other data.
state_population <- as.data.frame(read_csv('https://www2.census.gov/programs-surveys/popest/datasets/2010-2019/state/detail/SCPRC-EST2019-18+POP-RES.csv'))
Parsed with column specification:
cols(
SUMLEV = [31mcol_character()[39m,
REGION = [31mcol_character()[39m,
DIVISION = [31mcol_character()[39m,
STATE = [31mcol_character()[39m,
NAME = [31mcol_character()[39m,
POPESTIMATE2019 = [32mcol_double()[39m,
POPEST18PLUS2019 = [32mcol_double()[39m,
PCNT_POPEST18PLUS = [32mcol_double()[39m
)
state_population <- state_population %>%
filter(NAME != 'United States') %>%
filter(NAME != 'Puerto Rico Commonwealth') %>%
select(NAME, POPESTIMATE2019) %>%
rename(state = NAME, population = POPESTIMATE2019)
state_population
states_data_per_thousand <- inner_join(states_data, state_population, by='state') %>%
mutate(confirmed_per_thousand = 1000 * total_confirmed / population,
deaths_per_thousand = 1000 * total_deaths / population)
states_data_per_thousand
Awesome! Now we have data ready for both the social distancinng aspect of each state, and the cases and deaths for each state. It’s time to put it all together and visualize what’s going on here.
The Effects of Social Distancing Mandates on Coronavirus Cases and Deaths
First, let’s merge our two datsets so that we have one source to pull from for the rest of our analysis and visualization. We keep only the columns we need.
sd_data <- rownames_to_column(clustering_data, var = 'state')
combined_data <- inner_join(sd_data, states_data_per_thousand, by = 'state') %>%
select(state, `Social Distancing Mandates`, confirmed_per_thousand, deaths_per_thousand)
combined_data
combined_data <- combined_data %>%
rename(`Deaths (per thousand)` = deaths_per_thousand,
`Cases (per thousand)` = confirmed_per_thousand,
`Social Distancing` = `Social Distancing Mandates`) %>%
mutate(`Deaths (per thousand)` = round(`Deaths (per thousand)`,3),
`Cases (per thousand)` = round(`Cases (per thousand)`, 3),
text = paste('State: ', state,
'\nCases (per thousand): ', `Cases (per thousand)`,
'\nDeaths (per thousand): ', `Deaths (per thousand)`))
combined_data
We start by plotting deaths over cases, grouped by strictness of social distancing policies. Try hovering over these datapoints – you can see which state each point belongs to, and the relevant metrics.
scatter <- ggplot(combined_data, aes(x=`Cases (per thousand)`, y=`Deaths (per thousand)`, text=text)) +
geom_point(aes(color=`Social Distancing`))
ggsave('SocialDistancingScatter.png')
Saving 7 x 7 in image
interactive_scatter <- ggplotly(scatter, tooltip = 'text')
htmlwidgets::saveWidget(interactive_scatter, 'SocialDistancingScatter_Interactive.html')
interactive_scatter
It looks like there might be a difference in deaths per cases for each social distancing group, especially in the bottom left corner of our graph, where we have both groups plotted over a range of similar cases per thousand counts. Let’s throw some regression lines onto our data to see if this hypothesis holds any weight.
lines <- ggplot(combined_data, aes(x=`Cases (per thousand)`, y=`Deaths (per thousand)`)) +
geom_point(aes(color=`Social Distancing`)) +
geom_smooth(method=lm, formula = y~x, se=FALSE, aes(color=`Social Distancing`)) +
geom_smooth(method=lm, formula = y~x, se=FALSE, color = 'darkgray')
ggsave('SocialDistancingRegression.png')
Saving 7 x 7 in image
ggplotly(lines)
Yikes! In the plot above, the blue and red lines represent (respectively) the regression lines associated with the data for more strict and less strict states. The gray line is the regression for the entire dataset. But the gray line matches almost exactly with the blue line!! Why is this? Well, notice how far out from the rest of the data that the states are with the worst counts for cases and deaths. These massive outliers will absolutely dominating the regression, whether we are looking at a subset of the data or the entire thing. Since all of the outliers fall into the “More Strict” category, this causes the blue and gray lines to be very similar. (As an aside, it makes complete sense that all of the outlying states have more strict social distancing policies. Of course the states being impacted the most severely would be taking the greatest measures.)
Let’s take a quick peek at how many of these points can be classified as outliers, remove those from our dataset, and run a new regression.
cases_boxplot <- boxplot(combined_data$`Cases (per thousand)`,
horizontal = TRUE,
xlab = 'Cases (per thousand)',
main = 'Distribution of COVID-19 cases in the 50 states',
col = '#c288e3',
border = '#a18bad')

deaths_boxplot <- boxplot(combined_data$`Deaths (per thousand)`,
horizontal = TRUE,
xlab = 'Deaths (per thousand)',
main = 'Distribution of COVID-19 deaths in the 50 states',
col = '#ffe563',
border = '#b0ac9b')

combined_data$cases_rank <- rank(combined_data$`Cases (per thousand)`)
combined_data$deaths_rank <- rank(combined_data$`Deaths (per thousand)`)
no_outliers <- combined_data %>%
filter(cases_rank <= 46) %>%
filter(deaths_rank <= 45)
less_lines <- ggplot(no_outliers, aes(x=`Cases (per thousand)`, y=`Deaths (per thousand)`)) +
geom_point(aes(color=`Social Distancing`)) +
geom_smooth(method=lm, formula = y~x, se=TRUE, aes(color=`Social Distancing`))
ggsave('SocialDistancingRegression_NoOutliers.png')
Saving 7 x 7 in image
less_lines

This is interesting! It does appear that there may be a difference in slopes between our groups. We hesitate to make any broad claims about this without more rigorous statistical analysis, but for exploratory purposes it is at least thought-provoking. Even if it’s interesting though, is this the right question to ask? The slopes here would represent death rates of COVID-19, in terms of people who have contracted the disease.
President Trump thinks that there’s a more important measurement than death rates: number of deaths relative to population size. It’s not too hard for us to visualize that metric.
boxes <- ggplot(combined_data, aes(x = `Social Distancing`,
y = `Deaths (per thousand)`,
fill = `Social Distancing`)) +
geom_boxplot(color = 'darkgray') +
ggtitle('COVID-19 Deaths, by strictness of statewide social distancing policies') +
theme(legend.title = element_text(size = 10.5))
ggsave('SocialDistancingBoxplots.png')
Saving 7 x 7 in image
interactive_boxes <- ggplotly(boxes)
htmlwidgets::saveWidget(interactive_boxes, 'SocialDistancingBoxplots_Interactive.html')
interactive_boxes
At a glance, the only insight to be gathered from this graph is that there is much more variance in deaths per thousand among states with more strict social distancing policies. But go ahead, take advantage of the interactivity of this plot, and zoom in on the parts of the graph where our boxplots overlap. Looking closely, we see that little (if any) of the inner 50% of these two datasets is overlapping.
We perform a rough (admittedly sloppy) test to see if there is a difference in mean deaths per thousand between our two groups. We start with a histogram to visualize normality.
ggplot(combined_data, aes(x = `Deaths (per thousand)`, fill = `Social Distancing`)) +
geom_histogram(alpha = 0.6, color = 'gray')

Hmmmm… I would definitely want to do more rigorous analysis on the normality of this data. Potentially, it is adequately similar to a normal distribution, but (at least) the More Strict states are definitely right-skewed. We’ll go ahead with the test, but keep in mind that our assumptions for this test have not beenn fully fleshed out.
less_strict_deaths <- combined_data %>%
filter(`Social Distancing`=='Less Strict') %>%
select(`Deaths (per thousand)`)
more_strict_deaths <- combined_data %>%
filter(`Social Distancing`=='More Strict') %>%
select(`Deaths (per thousand)`)
t.test(less_strict_deaths, more_strict_deaths, alternative = 'two.sided', var.equal = FALSE)
Welch Two Sample t-test
data: less_strict_deaths and more_strict_deaths
t = -3.6345, df = 30.947, p-value = 0.0009989
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
-0.35296342 -0.09920801
sample estimates:
mean of x mean of y
0.06338095 0.28946667
Wowza, that’s a tiny p-value! At any reasonable level of significance, this shows that the data suggests a difference in mean deaths per thousand between the two groups.
LS0tCnRpdGxlOiAiU29jaWFsIERpc3RhbmNpbmcgRWZmZWN0aXZlbmVzcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBmaWxlIGVuZGVhdm9ycyB0byB2aXN1YWxpemUgdGhlIGVmZmVjdGl2ZW5lc3Mgb2Ygc29jaWFsIGRpc3RhbmNpbmcgbWVhc3VyZXMgYWdhaW5zdCB0aGUgQ09WSUQtMTkgcGFuZGVtaWMuIFdlIHBlcmZvcm0gay1tZWFucyBjbHVzdGVyIGFuYWx5c2lzIHRvIGlkZW50aWZ5IGNsYXNzaWZpY2F0aW9ucyBvZiBob3cgc3RyaWN0IHNvY2lhbCBkaXN0YW5jaW5nIGdvdmVybm1lbnQgbWFuZGF0ZXMgYXJlIGluIGVhY2ggb2YgdGhlIDUwIFUuUy4gU3RhdGVzLiBXZSB0aGVuIHVzZSB0aGVzZSBncm91cGluZ3MgdG8gdmlzdWFsaXplIGNvcm9uYXZpcnVzIGNhc2VzIGFuZCBkZWF0aHMgaW4gZWFjaCBzdGF0ZS4KCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShncmlkKQpsaWJyYXJ5KGdyaWRFeHRyYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShnZ3B1YnIpCmxpYnJhcnkodXNtYXApCmxpYnJhcnkoZmlmdHlzdGF0ZXIpCmBgYAoKIyBTb2NpYWwgRGlzdGFuY2luZyBEYXRhIFNvdXJjZXMKClRvIGJlZ2luIG91ciBhbmFseXNpcyBvZiB0aGUgZWZmZWN0aXZlbmVzcyBvZiBzb2NpYWwgZGlzdGFuY2luZywgd2UgbG9vayBhdCBhIGRhdGFzZXQgd2hpY2ggcmVwb3J0cyBpbmZvcm1hdGlvbiBhYm91dCB0aGUgbWVhc3VyZXMgbWFuZGF0ZWQgaW4gZWFjaCBvZiB0aGUgNTAgVS5TLiBTdGF0ZXMuIFtLRkZdKGh0dHBzOi8vd3d3LmtmZi5vcmcvY29yb25hdmlydXMtY292aWQtMTkvaXNzdWUtYnJpZWYvc3RhdGUtZGF0YS1hbmQtcG9saWN5LWFjdGlvbnMtdG8tYWRkcmVzcy1jb3JvbmF2aXJ1cy8jc29jaWFsZGlzdGFuY2luZykgaGFzIGEgZGF0YXNldCB3aXRoIGV4dGVuc2l2ZSByZWNvcmRzIGRldGFpbGluZyB0aGlzIGluZm9ybWF0aW9uLiBMZXQncyBiZWdpbiBieSBsb2FkaW5nIGluIHRoaXMgZGF0YXNldC4gSXQgY29udGFpbnMgYSBsb3Qgb2YgY29sdW1ucyB3aXRoIGEgbG90IG9mIGluZm9ybWF0aW9uLCBidXQgZm9yIG91ciBwdXJwb3NlcyB3ZSdyZSBnb2luZyB0byBmb2N1cyBvbiBMb2NhdGlvbiwgU3RheSBhdCBIb21lIE9yZGVyLCBMYXJnZSBHYXRoZXJpbmdzIEJhbiwgYW5kIFJlc3RhdXJhbnQgTGltaXRzLiBUaGVzZSAzIGNhdGVnb3JpZXMgd2VyZSBzZWxlY3RlZCBvdmVyIHRoZSBvdGhlcnMgYmFzZWQgb24gdGhlIG1hZ25pdHVkZSBvZiBhdmFpbGFibGUgZGF0YSwgdmFyaWFuY2UgYmV0d2VlbiBzdGF0ZXMsIGFuZCByZWxldmFuY2UgdG8gZW5mb3JjaW5nIHNvY2lhbCBkaXN0YW5jaW5nLiBQbGVhc2Ugbm90ZSB0aGF0IHdoaWxlIHRoaXMgZGF0YXNldCB3YXMgZ3JhYmJlZCBmcm9tIEtGRiBhcyBsaW5rZWQsIHRoZSBjc3Ygd2FzIG1pbGRseSBtYXNzYWdlZCBiZWZvcmUgbG9hZGluZyBpbnRvIFIgKHJlbW92aW5nIG1ldGFkYXRhIGxpbmVzLCBmb290bm90ZXMsIGVuc3VyaW5nIGNvbnNpc3RlbnQgbnVtYmVyIG9mIGNvbHVtbnMgaW4gZWFjaCByb3cpLiBUaGlzIGRhdGEgd2FzIGRvd25sb2FkZWQgb24gYW5kIGFjY3VyYXRlIGFzIG9mIE1heSAxMiwgMjAyMC4KCmBgYHtyfQpzb2NpYWxfZGlzdGFuY2luZyA8LSBhcy5kYXRhLmZyYW1lKHJlYWRfY3N2KCcuL3NvY2lhbF9kaXN0YW5jaW5nLmNzdicpKQpyb3cubmFtZXMoc29jaWFsX2Rpc3RhbmNpbmcpIDwtIHNvY2lhbF9kaXN0YW5jaW5nJExvY2F0aW9uCnNvY2lhbF9kaXN0YW5jaW5nIDwtIHNvY2lhbF9kaXN0YW5jaW5nICU+JSBzZWxlY3QoJ0xvY2F0aW9uJywnU3RheSBhdCBIb21lIE9yZGVyJywnTGFyZ2UgR2F0aGVyaW5ncyBCYW4nLCdSZXN0YXVyYW50IExpbWl0cycpCnNvY2lhbF9kaXN0YW5jaW5nWzE6MTAsXQpgYGAKCgojIyBUcmFuc2Zvcm1pbmcgQ2F0ZWdvcmljYWwgVmFyaWFibGVzCgpPdXIgZ29hbCBpcyB0byB1c2Ugay1tZWFucyBjbHVzdGVyaW5nIGFuYWx5c2lzIHRvIGlkZW50aWZ5IGNsYXNzaWZpY2F0aW9ucyBvZiBob3cgc3RyaWN0IGVhY2ggc3RhdGUgaXMgd2l0aCB0aGVpciBzb2NpYWwgZGlzdGFuY2luZyBwb2xpY2llcy4gSG93ZXZlciwgb3VyIGRhdGEgaW4gaXQncyBjdXJyZW50IHN0YXRlIGNvbnRhaW5zIG9ubHkgY2F0ZWdvcmljYWwgdmFyaWFibGVzLCBhbmQgay1tZWFucyBjbHVzdGVyaW5nIHJlcXVpcmVzIHRoZSB1c2Ugb2YgbnVtZXJpYyB2YXJpYWJsZXMuIEx1Y2tpbHksIHRoZSBjYXRlZ29yaWVzIHdlIGhhdmUgYXJlIGluaGVyZW50bHkgb3JkaW5hbCwgc28gbGV0J3MgZGVmaW5lIGEgbWFwcGluZyBmb3IgZWFjaCB2YXJpYWJsZSB0byBiZWNvbWUgIm51bWVyaWNhbCIuIFdlIHdpbGwgdGFrZSB0aGUgY2F0ZWdvcmllcyBmb3IgZWFjaCB2YXJpYWJsZSwgYW5kIGFzc2lnbiB0aGVtIGFuIGludGVnZXIgdmFsdWUgYmV0d2VlbiAwIGFuZCAxMCwgbG9vc2VseSBkZWZpbmVkIGFzIDAgYmVpbmcgdGhlICJsZWFzdCBzdHJpY3QiIGFuZCAxMCBiZWluZyB0aGUgIm1vc3Qgc3RyaWN0Ii4gS2VlcGluZyBhbGwgMyBwcmVkaWN0b3IgdmFyaWFibGVzIG9uIGEgc2ltaWxhciBzY2FsZSBhbGxvd3Mgay1tZWFucyB0byBlZmZlY3RpdmVseSBhbmFseXplIGNsdXN0ZXJzIHdpdGhvdXQgYWxsb3dpbmcgYW55IHNpbmdsZSB2YXJpYWJsZSB0byBvdmVycnVuIHRoZSBhbmFseXNpcy4gCgpBcyBhIG5vdGUsIHRoaXMgdHlwZSBvZiB0cmFuc2Zvcm1hdGlvbiBpcyBoaWdobHkgc3ViamVjdGl2ZTogbW9yZSBvZiBhbiBhcnQgZm9ybSB0aGFuIGEgc2NpZW5jZS4gQSBkaWZmZXJlbnQgYW5hbHlzdCBtYXkgaGF2ZSBkaWZmZXJlbnQgaWRlYXMgYWJvdXQgaG93IHRvIGFzc2lnbiB3ZWlnaHQgdG8gdGhlIGxldmVscyBvZiBlYWNoIGZhY3Rvci4gUmVnYXJkbGVzcywgcGVyaGFwcyB0aGUgbW9zdCBpbXBvcnRhbnQgcHJpbmNpcGxlcyB0byBmb2xsb3cgZHVyaW5nIHRoaXMgcHJvY2VzcyBpcyB0byBrZWVwIGVhY2ggdmFyaWFibGUgb24gc2NhbGVzIG9mIHNpbWlsYXIgbWFnbml0dWRlLgoKIyMjIFRyYW5zZm9ybWluZyAiU3RheSBhdCBIb21lIE9yZGVyIgpgYGB7cn0KdW5pcXVlKHNvY2lhbF9kaXN0YW5jaW5nWywnU3RheSBhdCBIb21lIE9yZGVyJ10pCmBgYAoKV2UgY2FuIHNlZSBoZXJlIHRoYXQgdGhlcmUgYXJlIDQgY2F0ZWdvcmllcyBvZiByZXNwb25zZXMgdG8gdGhlICJTdGF5IGF0IEhvbWUiIHZhcmlhYmxlOiBMaWZ0ZWQsIEhpZ2gtUmlzayBHcm91cHMsIFJvbGxlZCBCYWNrIHRvIEhpZ2ggUmlzayBHcm91cHMsIGFuZCBTdGF0ZXdpZGUuIFRoZXJlIGlzIGFsc28gYSAiLSIgcmVzcG9uc2UsIGluZGljYXRpbmcgc29tZSB0eXBlIG9mIG1pc3NpbmcgdmFsdWUuIFdoaWNoIHN0YXRlcyBvbmx5IHJlY29yZCAiLSIgZm9yIFN0YXkgYXQgSG9tZSBPcmRlcj8KYGBge3J9CmZpbHRlcihzb2NpYWxfZGlzdGFuY2luZyxgU3RheSBhdCBIb21lIE9yZGVyYD09Jy0nKQpgYGAKClNvbWUgW291dHNpZGUgcmVzZWFyY2hdKGh0dHBzOi8vd3d3LmNubi5jb20vMjAyMC8wNC8xMy9wb2xpdGljcy9hc2EtaHV0Y2hpc29uLWFya2Fuc2FzLWNvcm9uYXZpcnVzL2luZGV4Lmh0bWwpIGluZGljYXRlcyB0aGF0IHRoZSB2YWx1ZSBpcyBtaXNzaW5nIGJlY2F1c2UgdGhlc2Ugc3RhdGVzIGFjdHVhbGx5IGhhdmUgKipubyBzdGF5IGF0IGhvbWUgb3JkZXIqKiEgVGhpcyBtdXN0IGJlIHRoZSBtZWFuaW5nIG9mICItIi4gVHVybnMgb3V0IHRoZW4sIHdlIGFjdHVhbGx5IGhhdmUgNSBjYXRlZ29yaWVzIGZvciB0aGlzIHZhcmlhYmxlLiAKClRoaXMgaXMgd2hlcmUgaXQgYmVjb21lcyBzdWJqZWN0aXZlLCBidXQgd2Ugd2lsbCBkbyBvdXIgYmVzdCB0byBjcmVhdGUgYSBmYWlyIG1hcHBpbmcgYmV0d2VlbiB0aGlzIHNvbWV3aGF0LW9yZGluYWwgZGF0YSBhbmQgYSBudW1lcmljYWwgMC0xMCBzY2FsZS4gQmVsb3cgd2UgYXNzaWduIGVhY2ggY2F0ZWdvcnkgb2YgIlN0YXkgYXQgSG9tZSBPcmRlciIgdG8gYSBudW1lcmljYWwgdmFsdWUsIGFuZCBjcmVhdGUgYSBuZXcgY29sdW1uIHRvIHJlY29yZCB0aGVzZS4KYGBge3J9CiMgUm9sbGVkIEJhY2sgdG8gSGlnaCBSaXNrIEdyb3VwcyBpcyBjbGFzc2lmaWVkIGFzIG1vcmUgc3RyaWN0IHRoYW4gSGlnaC1SaXNrIGdyb3VwcwojIGJlY2F1c2Ugc3RhdGVzIGluIHRoaXMgY2xhc3NpZmljYXRpb24gYXQgb25lIHBvaW50IGhhZCBzdGF0ZXdpZGUgc3RheSBhdCBob21lIG9yZGVycwoKc29jaWFsX2Rpc3RhbmNpbmckc3RheV9ob21lIDwtIGlmZWxzZShzb2NpYWxfZGlzdGFuY2luZyRgU3RheSBhdCBIb21lIE9yZGVyYD09Jy0nLCAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNvY2lhbF9kaXN0YW5jaW5nJGBTdGF5IGF0IEhvbWUgT3JkZXJgPT0nTGlmdGVkJywgMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzb2NpYWxfZGlzdGFuY2luZyRgU3RheSBhdCBIb21lIE9yZGVyYD09J0hpZ2gtUmlzayBHcm91cHMnLCA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNvY2lhbF9kaXN0YW5jaW5nJGBTdGF5IGF0IEhvbWUgT3JkZXJgPT0nUm9sbGVkIEJhY2sgdG8gSGlnaCBSaXNrIEdyb3VwcycsIDgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc29jaWFsX2Rpc3RhbmNpbmckYFN0YXkgYXQgSG9tZSBPcmRlcmA9PSdTdGF0ZXdpZGUnLCAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEpKSkpKQpzb2NpYWxfZGlzdGFuY2luZwpgYGAKCiMjIyBUcmFuc2Zvcm1pbmcgIkxhcmdlIEdhdGhlcmluZ3MgQmFuIgoKV2Ugd2lsbCBmb2xsb3cgc2ltaWxhciBzdGVwcyB0byB0cmFuc2Zvcm0gdGhlICJMYXJnZSBHYXRoZXJpbmdzIEJhbiIgcHJlZGljdG9yIHZhcmlhYmxlLgpgYGB7cn0KdW5pcXVlKHNvY2lhbF9kaXN0YW5jaW5nWywnTGFyZ2UgR2F0aGVyaW5ncyBCYW4nXSkKYGBgCgpNb3N0IG9mIHRoZXNlIGNhdGVnb3JpZXMgc2VlbSB0byBtYWtlIHNlbnNlIGFuZCBhcmUgZmFpcmx5IG9yZGluYWwuIEhvd2V2ZXIsIHR3byBvZiB0aGUgY2F0ZWdvcmllcyBhcmUgdW5jbGVhciBpbiB0aGVpciBtZWFuaW5nOiAnLScgYW5kICdPdGhlcicuIExldCdzIGxvb2sgYXQgd2hpY2ggc3RhdGVzIGZhbGwgaW50byB0aGVzZSBjYXRlZ29yaWVzIGFuZCBzZWUgaWYgd2UgY2FuIGxlYXJuIG1vcmUgaW5mb3JtYXRpb24uCmBgYHtyfQpmaWx0ZXIoc29jaWFsX2Rpc3RhbmNpbmcsIGBMYXJnZSBHYXRoZXJpbmdzIEJhbmA9PSctJykKYGBgCgpEb2luZyBzb21lIG91dHNpZGUgcmVzZWFyY2gsIGl0IGFwcGVhcnMgW01pbm5lc290YSBoYXMgYmFubmVkIGxhcmdlIGdhdGhlcmluZ3MgZm9yIGhpZ2ggc2Nob29sIGdyYWR1YXRpb24gY2VyZW1vbmllc10oaHR0cHM6Ly93d3cubWlubnBvc3QuY29tL2hlYWx0aC8yMDIwLzA1L3RoZS1kYWlseS1jb3JvbmF2aXJ1cy11cGRhdGUtbWlubmVzb3RhLXN1cnBhc3Nlcy0xMDAwMC1jb25maXJtZWQtY2FzZXMtc3RhdGUtYmFucy1sYXJnZS1pbi1wZXJzb24tZ3JhZHVhdGlvbi1jZXJlbW9uaWVzLyksIGJ1dCBoYXMgbWFkZSBubyBnZW5lcmFsICptYW5kYXRlcyogZm9yIGxhcmdlIGdhdGhlcmluZ3Mgb3V0c2lkZSBvZiB0aGF0LCBvbmx5IHN1Z2dlc3Rpb25zLiBJdCBhcHBlYXJzIE5vcnRoIERha290YSBoYXMgW25vdCBtYWRlIGEgbWFuZGF0ZV0oaHR0cHM6Ly93d3cuYWFycC5vcmcvcG9saXRpY3Mtc29jaWV0eS9nb3Zlcm5tZW50LWVsZWN0aW9ucy9pbmZvLTIwMjAvY29yb25hdmlydXMtc3RhdGUtcmVzdHJpY3Rpb25zLmh0bWwpIGNvbmNlcm5pbmcgbGFyZ2UgZ2F0aGVyaW5ncy4KCmBgYHtyfQpmaWx0ZXIoc29jaWFsX2Rpc3RhbmNpbmcsIGBMYXJnZSBHYXRoZXJpbmdzIEJhbmA9PSdPdGhlcicpCmBgYAoKUmVzZWFyY2ggaW5kaWNhdGVzIHRoYXQgW0Nvbm5lY3RpY3V0IGlzIGJhbm5pbmcgZ2F0aGVyaW5ncyB3aXRoID41IHBlb3BsZV0oaHR0cHM6Ly93d3cuY3Rwb3N0LmNvbS9uZXdzL2Nvcm9uYXZpcnVzL2FydGljbGUvR292LUxhbW9udC13YW50cy1kaXNhc3Rlci1kZWNsYXJhdGlvbi1mcm9tLVRydW1wLTE1MTU5MjA4LnBocCksIFtGbG9yaWRhIHByb2hpYml0cyBnYXRoZXJpbmdzIG9mIDEwKyBvbiBiZWFjaGVzIG9ubHldKGh0dHBzOi8vd3d3LnBvc3QtZ2F6ZXR0ZS5jb20vbmV3cy9uYXRpb24vMjAyMC8wMy8xNy9GbG9yaWRhLWJlYWNoZXMtcmVzdGF1cmFudHMtYmFycy1zY2hvb2xzLXN0dWRlbnRzLWdvdmVybm9yLWNvcm9uYXZpcnVzL3N0b3JpZXMvMjAyMDAzMTcwMjAyKSwgYW5kIFtSaG9kZSBJc2xhbmQgYmFucyBnYXRoZXJpbmdzIG9mIDEwK10oaHR0cHM6Ly93d3cucmkuZ292L3ByZXNzL3ZpZXcvMzgwMzMpLgoKQmFzZWQgb24gdGhlc2UgZmluZGluZ3MgZm9yIE1OLCBORCwgQ1QsIEZMLCBhbmQgUkksIHdlIGRlZmluZSBuZXcgY2F0ZWdvcmllcyBvZiAiTm9uZSIsICJTcGVjaWFsIHR5cGVzIG9mIGdhdGhlcmluZ3MgcHJvaGliaXRlZCIsIGFuZCAiPjUgUGVvcGxlIFByb2hpYml0ZWQiLiBXZSB3aWxsIHRoZW4gcmUtY2xhc3NpZnkgdGhlICJMYXJnZSBHYXRoZXJpbmdzIEJhbiIgYXR0cmlidXRlIGluIHdheXMgdGhhdCBtYWtlIHNlbnNlIGZvciB0aGVzZSA1IHN0YXRlcy4KYGBge3J9CnNvY2lhbF9kaXN0YW5jaW5nWydNaW5uZXNvdGEnLCAnTGFyZ2UgR2F0aGVyaW5ncyBCYW4nXSA8LSAnU3BlY2lhbCB0eXBlcyBvZiBnYXRoZXJpbmdzIHByb2hpYml0ZWQnCnNvY2lhbF9kaXN0YW5jaW5nWydOb3J0aCBEYWtvdGEnLCAnTGFyZ2UgR2F0aGVyaW5ncyBCYW4nXSA8LSAnTm9uZScKc29jaWFsX2Rpc3RhbmNpbmdbJ0Nvbm5lY3RpY3V0JywgJ0xhcmdlIEdhdGhlcmluZ3MgQmFuJ10gPC0gJz41IFBlb3BsZSBQcm9oaWJpdGVkJwpzb2NpYWxfZGlzdGFuY2luZ1snRmxvcmlkYScsICdMYXJnZSBHYXRoZXJpbmdzIEJhbiddIDwtICdTcGVjaWFsIHR5cGVzIG9mIGdhdGhlcmluZ3MgcHJvaGliaXRlZCcKc29jaWFsX2Rpc3RhbmNpbmdbJ1Job2RlIElzbGFuZCcsICdMYXJnZSBHYXRoZXJpbmdzIEJhbiddIDwtICc+MTAgUGVvcGxlIFByb2hpYml0ZWQnCmBgYAoKTGV0J3MgbG9vayBhdCB0aGUgdW5pcXVlIGxldmVscyBvZiB0aGlzIGZhY3RvciBvbmUgbW9yZSB0aW1lIHRvIG1ha2Ugc3VyZSBub3RoaW5nIHdlbnQgYXdyeS4KYGBge3J9CnVuaXF1ZShzb2NpYWxfZGlzdGFuY2luZ1ssJ0xhcmdlIEdhdGhlcmluZ3MgQmFuJ10pCmBgYAoKR3JlYXQhIE5vdyBsZXQncyBnbyBhaGVhZCBhbmQgYXNzaWduIG51bWVyaWNhbCBzY29yZXMgdG8gZWFjaCBvZiB0aGVzZSBsZXZlbHMsIGNyZWF0aW5nIGEgbmV3IGNvbHVtbiBpbiBvdXIgZGF0YWZyYW1lIHRvIHN0b3JlIHRob3NlIHZhbHVlcy4gTm90aWNlIHRoYXQgJ0V4cGFuZGVkIHRvID4yNSBQZW9wbGUgUHJvaGliaXRlZCcgYW5kICdFeHBhbmRlZCB0byAyNSsgUGVvcGxlIFByb2hpYml0ZWQnIGFyZSB0d28gZGlmZmVyZW50IGxhYmVscywgYnV0IHdlIHdpbGwgdHJlYXQgdGhlc2UgYXMgZXF1YWxseSBzdHJpY3QgY2F0ZWdvcmllcy4KYGBge3J9CnNvY2lhbF9kaXN0YW5jaW5nJGxhcmdlX2dhdGhlcmluZ3MgPC0gaWZlbHNlKHNvY2lhbF9kaXN0YW5jaW5nJGBMYXJnZSBHYXRoZXJpbmdzIEJhbmA9PSdOb25lJywgMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc29jaWFsX2Rpc3RhbmNpbmckYExhcmdlIEdhdGhlcmluZ3MgQmFuYD09J0xpZnRlZCcsIDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNvY2lhbF9kaXN0YW5jaW5nJGBMYXJnZSBHYXRoZXJpbmdzIEJhbmA9PSdTcGVjaWFsIHR5cGVzIG9mIGdhdGhlcmluZ3MgcHJvaGliaXRlZCcsMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc29jaWFsX2Rpc3RhbmNpbmckYExhcmdlIEdhdGhlcmluZ3MgQmFuYD09J0V4cGFuZGVkIHRvIDUwKyBQZW9wbGUgUHJvaGliaXRlZCcsIDQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNvY2lhbF9kaXN0YW5jaW5nJGBMYXJnZSBHYXRoZXJpbmdzIEJhbmA9PSdFeHBhbmRlZCB0byAyNSsgUGVvcGxlIFByb2hpYml0ZWQnLCA1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzb2NpYWxfZGlzdGFuY2luZyRgTGFyZ2UgR2F0aGVyaW5ncyBCYW5gPT0nRXhwYW5kZWQgdG8gPjI1IFBlb3BsZSBQcm9oaWJpdGVkJywgNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc29jaWFsX2Rpc3RhbmNpbmckYExhcmdlIEdhdGhlcmluZ3MgQmFuYD09J0V4cGFuZGVkIHRvIDIwKyBQZW9wbGUgUHJvaGliaXRlZCcsIDYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNvY2lhbF9kaXN0YW5jaW5nJGBMYXJnZSBHYXRoZXJpbmdzIEJhbmA9PSdFeHBhbmRlZCB0byA+MTAgUGVvcGxlIFByb2hpYml0ZWQnLCA3LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzb2NpYWxfZGlzdGFuY2luZyRgTGFyZ2UgR2F0aGVyaW5ncyBCYW5gPT0nPjEwIFBlb3BsZSBQcm9oaWJpdGVkJywgOCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc29jaWFsX2Rpc3RhbmNpbmckYExhcmdlIEdhdGhlcmluZ3MgQmFuYD09Jz41IFBlb3BsZSBQcm9oaWJpdGVkJywgOSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc29jaWFsX2Rpc3RhbmNpbmckYExhcmdlIEdhdGhlcmluZ3MgQmFuYD09J0FsbCBHYXRoZXJpbmdzIFByb2hpYml0ZWQnLCAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQSkpKSkpKSkpKSkpCnNvY2lhbF9kaXN0YW5jaW5nCmBgYAoKIyMjIFRyYW5zZm9ybWluZyAiUmVzdGF1cmFudCBMaW1pdHMiCgpGaW5hbGx5LCB3ZSB0cmFuc2Zvcm0gdGhlICJSZXN0YXVyYW50IExpbWl0cyIgcHJlZGljdG9yIHZhcmlhYmxlLgpgYGB7cn0KdW5pcXVlKHNvY2lhbF9kaXN0YW5jaW5nWywnUmVzdGF1cmFudCBMaW1pdHMnXSkKYGBgCgpgYGB7cn0KZmlsdGVyKHNvY2lhbF9kaXN0YW5jaW5nLCBgUmVzdGF1cmFudCBMaW1pdHNgPT0nLScpCmBgYAoKU291dGggRGFrb3RhIGlzIHRoZSBvbmx5IHN0YXRlIHdpdGhvdXQgYSBjbGVhcmx5IGRlZmluZWQgZW50cnkgZm9yIFJlc3RhdXJhbnQgTGltaXRzLCBidXQgc2luY2UgdGhlcmUgaXMgW25vIHN0YXRld2lkZSBiYW4gb24gcmVzdGF1cmFudHMgaW4gU291dGggRGFrb3RhXShodHRwczovL3d3dy5rZWxvbGFuZC5jb20va2Vsb2xhbmQtY29tLW9yaWdpbmFsL3doby1oYXMtdGhlLWF1dGhvcml0eS10by1jbG9zZS1yZXN0YXVyYW50cy1hbmQtYmFycy1pbi1zb3V0aC1kYWtvdGEtc3RhdGUtYW5kLWxvY2FsLWxlYWRlcnMtcG9pbnQtYXQtZWFjaC1vdGhlci8pLCB3ZSB3aWxsIGp1c3QgYXNzaWduIHRoZSAiLSIgdmFsdWUgYSB2ZXJ5IGxvdyBzY29yZSB3aGVuIHdlIGNyZWF0ZSB0aGUgbnVtZXJpYyBjb2x1bW5zLgpgYGB7cn0Kc29jaWFsX2Rpc3RhbmNpbmckcmVzdGF1cmFudHMgPC0gaWZlbHNlKHNvY2lhbF9kaXN0YW5jaW5nJGBSZXN0YXVyYW50IExpbWl0c2A9PSctJywgMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNvY2lhbF9kaXN0YW5jaW5nJGBSZXN0YXVyYW50IExpbWl0c2A9PSdMaW1pdGVkIERpbmUtaW4gU2VydmljZScsIDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzb2NpYWxfZGlzdGFuY2luZyRgUmVzdGF1cmFudCBMaW1pdHNgPT0nUmVvcGVuZWQgdG8gRGluZS1pbiBTZXJ2aWNlJywgNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHNvY2lhbF9kaXN0YW5jaW5nJGBSZXN0YXVyYW50IExpbWl0c2A9PSdSZW9wZW5lZCB0byBEaW5lLWluIFNlcnZpY2Ugd2l0aCBDYXBhY2l0eSBMaW1pdHMnLCA3LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2Uoc29jaWFsX2Rpc3RhbmNpbmckYFJlc3RhdXJhbnQgTGltaXRzYD09J0Nsb3NlZCBFeGNlcHQgZm9yIFRha2VvdXQvRGVsaXZlcnknLCAxMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEpKSkpKQpzb2NpYWxfZGlzdGFuY2luZwpgYGAKCiMgSy1NZWFucyBDbHVzdGVyaW5nCgpOb3cgdGhhdCBvdXIgZGF0YXNldCBpcyBpbiBhIGhlbHBmdWxseSBudW1lcmljIGZvcm0sIHdlIGNhbiBwZXJmb3JtIGstbWVhbnMgY2x1c3RlcmluZyBhbmFseXNpcy4gRm9yIHRoZSBzYWtlIG9mIGNvbnZlbmllbmNlLCBsZXQncyBkZWZpbmUgYSBzdWJzZXQgb2YgdGhlIGRhdGEgdGhhdCBvbmx5IGNvbnRhaW5zIHRoZSByZWxldmFudCBjb2x1bW5zLgpgYGB7cn0KY2x1c3RlcmluZ19kYXRhIDwtIHNvY2lhbF9kaXN0YW5jaW5nWyxjKCJzdGF5X2hvbWUiLCAibGFyZ2VfZ2F0aGVyaW5ncyIsICJyZXN0YXVyYW50cyIpXQpjbHVzdGVyaW5nX2RhdGEKYGBgCgpOb3cgbGV0J3MgdHJ5IHRvIGlkZW50aWZ5IHRoZSBvcHRpbWFsIG51bWJlciBvZiBjbHVzdGVycy4gV2UnbGwgY2hlY2sgZnJvbSAxIHRvIDE1IGNsdXN0ZXJzCmBgYHtyfQp3c3MgPC0gbnVtZXJpYygxNSkKZm9yIChrIGluIDE6MTUpIHdzc1trXSA8LSBzdW0oa21lYW5zKGNsdXN0ZXJpbmdfZGF0YSwgY2VudGVycyA9IGssIG5zdGFydCA9IDMwKSR3aXRoaW5zcykKcGxvdCgxOjE1LCB3c3MsIHR5cGUgPSAiYiIsIHhsYWIgPSAiTnVtYmVyIG9mIENsdXN0ZXJzIiwgeWxhYiA9ICJXaXRoaW4gU3VtIG9mIFNxdWFyZXMiKQpgYGAKCmBgYHtyfQprbSA8LSBrbWVhbnMoY2x1c3RlcmluZ19kYXRhLCAyLCBuc3RhcnQgPSAyNSkKa20KYGBgCgpMZXQncyB2aXN1YWxpemUgb3VyIGRhdGEuIFNpbmNlIGFsbCBwb2ludHMgYXJlIGxpa2VseSB0byBiZSBvbiB0b3Agb2YgZWFjaCBvdGhlciwgd2UgcGxvdCB3aXRoIHNvbWUgaml0dGVyLgpgYGB7cn0KZGYgPC0gYXMuZGF0YS5mcmFtZShjbHVzdGVyaW5nX2RhdGEpCmRmJGNsdXN0ZXIgPC0gIGZhY3RvcihrbSRjbHVzdGVyKQpjZW50ZXJzIDwtIGFzLmRhdGEuZnJhbWUoa20kY2VudGVycykKCnNobGcgPC0gZ2dwbG90KGRhdGEgPSBkZiwgYWVzKHggPSBzdGF5X2hvbWUsIHkgPSBsYXJnZV9nYXRoZXJpbmdzLCBjb2xvciA9IGNsdXN0ZXIpKSArCiAgZ2VvbV9qaXR0ZXIoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBjZW50ZXJzLCBhZXMoeCA9IHN0YXlfaG9tZSwgeSA9IGxhcmdlX2dhdGhlcmluZ3MsIGNvbG9yID0gYXMuZmFjdG9yKGMoMSwyKSkpLAogICAgICAgICAgICAgc2l6ZSA9IDEwLCBhbHBoYSA9IC4zLCBzaG93LmxlZ2VuZCA9IEZBTFNFKQoKc2hyIDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh4ID0gc3RheV9ob21lLCB5ID0gcmVzdGF1cmFudHMsIGNvbG9yID0gY2x1c3RlcikpICsKICBnZW9tX2ppdHRlcigpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gInJpZ2h0IikgKwogIGdlb21fcG9pbnQoZGF0YSA9IGNlbnRlcnMsIGFlcyh4ID0gc3RheV9ob21lLCB5ID0gcmVzdGF1cmFudHMsIGNvbG9yID0gYXMuZmFjdG9yKGMoMSwyKSkpLAogICAgICAgICAgICAgc2l6ZSA9IDEwLCBhbHBoYSA9IC4zLCBzaG93LmxlZ2VuZCA9IEZBTFNFKQoKbGdyIDwtIGdncGxvdChkYXRhID0gZGYsIGFlcyh5ID0gbGFyZ2VfZ2F0aGVyaW5ncywgeCA9IHJlc3RhdXJhbnRzLCBjb2xvciA9IGNsdXN0ZXIpKSArCiAgZ2VvbV9qaXR0ZXIoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIpICsKICBnZW9tX3BvaW50KGRhdGEgPSBjZW50ZXJzLCBhZXMoeSA9IGxhcmdlX2dhdGhlcmluZ3MsIHggPSByZXN0YXVyYW50cywgY29sb3IgPSBhcy5mYWN0b3IoYygxLDIpKSksCiAgICAgICAgICAgICBzaXplID0gMTAsIGFscGhhID0gLjMsIHNob3cubGVnZW5kID0gRkFMU0UpCgpncmlkLmFycmFuZ2Uoc2hsZyArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksIAogICAgICAgICAgICAgc2hyICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSwKICAgICAgICAgICAgIGxnciArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiksCiAgICAgICAgICAgICB0b3AgPSAiU29jaWFsIERpc3RhbmNpbmcgQ2x1c3RlcnMiLCBuY29sID0gMSkKYGBgCgpUaGlzIGlzIHZlcnkgcmF3IGRhdGEgKGV2ZW4gZ3JhcGhlZCBpbiBpdCdzICJudW1lcmljYWwiIGZvcm1hdCBkZXNwaXRlIGJlaW5nIHJlcHJlc2VudGF0aXZlIG9mIGNhdGVnb3JpY2FsIGRhdGEpLCBzbyB3ZSBkZWZpbml0ZWx5IHdvbid0IGluY2x1ZGUgdGhlbSBpbiB0aGUgZmluYWwgcHJlc2VudGF0aW9uLiBTdGlsbCwgdGhleSBhcmUgaWxsdXN0cmF0aXZlIGluIHNob3dpbmcgaG93IHdlbGwgdGhlIGNsdXN0ZXJpbmcgYWxnb3JpdGhtIHdvcmtzLCBhbmQgaWYgdGhlIGNsdXN0ZXJzIHJlYWxseSBhcmUgZGlzdGluY3QuIEl0IGFwcGVhcnMgdGhhdCBzdGF5IGF0IGhvbWUgb3JkZXJzIHdlcmUgdGhlIGJpZ2dlc3QgZGl2aWRpbmcgZmFjdG9yIGJldHd0ZWVuIHN0YXRlcywgYnV0IHJlc3RhdXJhbnRzIGFuZCBsYXJnZSBnYXRoZXJpbmdzIG1haW50YWluZWQgKnNvbWUqIHByZWRpY3RpdmUgcG93ZXIgYXMgd2VsbC4gTGV0J3MgYWRkIG9uZSBtb3JlIGNvbHVtbiB0byBvdXIgZGF0YXNldCB0byBkaWZmZXJlbnRpYXRlIGJldHdlZW4gIk1vcmUgU3RyaWN0IiBhbmQgIkxlc3MgU3RyaWN0IiBmb3IgIlNvY2lhbCBEaXN0YW5jaW5nIE1hbmRhdGVzIi4KCmBgYHtyfQppZihrbSRjZW50ZXJzWzFdIDwga20kY2VudGVyc1syXSl7CiAgY2x1c3Rlcl90aXRsZXMgPC0gYygnTGVzcyBTdHJpY3QnLCAnTW9yZSBTdHJpY3QnKQp9IGVsc2UgewogIGNsdXN0ZXJfdGl0bGVzIDwtIGMoJ01vcmUgU3RyaWN0JywgJ0xlc3MgU3RyaWN0JykKfQoKaWRlbnRpZnlfY2x1c3RlciA8LSBmdW5jdGlvbihudW0pewogIHJldHVybihjbHVzdGVyX3RpdGxlc1tudW1dKQp9CgpjbHVzdGVyaW5nX2RhdGEkYFNvY2lhbCBEaXN0YW5jaW5nIE1hbmRhdGVzYCA8LSBpZGVudGlmeV9jbHVzdGVyKGttJGNsdXN0ZXIpCmNsdXN0ZXJpbmdfZGF0YQpgYGAKCkF3ZXNvbWUhIE5vdyB3ZSBoYXZlIGEgZGVjZW50IGNsYXNzaWZpY2F0aW9uIG9mIGhvdyBzdHJpY3QgZWFjaCBvZiB0aGUgVS5TLiBTdGF0ZXMgaGF2ZSBiZWVuIHdpdGggdGhlaXIgc3RhdGV3aWRlIHNvY2lhbCBkaXN0YW5jaW5nIG1hbmRhdGVzLCBhbmQgd2UgYXJlIHJlYWR5IHRvIHRha2UgdGhpcyBkYXRhIGFuZCBjb21wYXJlIGl0IHRvIGhvdyB3ZWxsIGVhY2ggc3RhdGUgaXMgZG9pbmcgaW4gdGVybXMgb2YgY2FzZXMgYW5kLCBlc3BlY2lhbGx5LCBkZWF0aHMuIEZpcnN0LCBsZXQncyBxdWlja2x5IHZpc3VhbGl6ZSB0aGlzIGRhdGEgdG8gc2VlIHdoZXJlIGVhY2ggc3RhdGUgZW5kZWQgdXAuCmBgYHtyfQpwbG90X3NkX2RhdGEgPC0gY2x1c3RlcmluZ19kYXRhICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbih2YXIgPSAnc3RhdGUnKSAlPiUKICByZW5hbWUoc29jaWFsX2Rpc3RhbmNpbmcgPSBgU29jaWFsIERpc3RhbmNpbmcgTWFuZGF0ZXNgKSAlPiUKICByaWdodF9qb2luKHN0YXRlcywgYnkgPSBjKCdzdGF0ZScgPSAnc3RhdGVfbmFtZScpKQoKc2RfbWFwIDwtIGdncGxvdChwbG90X3NkX2RhdGEsIGFlcyhtYXBfaWQgPSB0b2xvd2VyKHN0YXRlKSkpICsgCiAgZ2VvbV9tYXAoYWVzKGZpbGwgPSBzb2NpYWxfZGlzdGFuY2luZyksIGNvbG9yID0gJ3doaXRlJywgbWFwID0gZmlmdHlfc3RhdGVzKSArIAogIGV4cGFuZF9saW1pdHMoeCA9IGZpZnR5X3N0YXRlcyRsb25nLCB5ID0gZmlmdHlfc3RhdGVzJGxhdCkgKwogIGNvb3JkX21hcCgpICsKICBsYWJzKHggPSAiIiwgeSA9ICIiLCBmaWxsID0gJ1NvY2lhbCBEaXN0YW5jaW5nJykgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLCAKICAgICAgICBwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9ibGFuaygpKQoKZ2dzYXZlKCdTb2NpYWxEaXN0YW5jaW5nTWFwLnBuZycpCnNkX21hcApgYGAKYGBge3J9CnN0YXRlcwpgYGAKCgojIENhc2VzIGFuZCBEZWF0aHMgRGF0YSBTb3VyY2UKClRvIG9idGFpbiBpbmZvcm1hdGlvbiByZWdhcmRpbmcgZGVhdGhzIGFuZCBjYXNlcywgd2Ugd2lsbCB1c2UgdGhlIEpvaG5zIEhvcGtpbnMgZGF0YSBzb3VyY2VzLiBTaW5jZSBvdXIgc29jaWFsIGRpc3RhbmNpbmcgZGF0YSB3YXMgY3VycmVudCBhcyBvZiBNYXkgMTIsIHdlIHdpbGwgdXNlIGRhdGEgZnJvbSB0aGF0IHNhbWUgZGF5IGZvciB0aGUgc2FrZSBvZiBjb25zaXN0ZW5jeS4KYGBge3J9CmpvaG5zX2hvcGtpbnNfZGF0YSA8LSBhcy5kYXRhLmZyYW1lKHJlYWRfY3N2KCdodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQ1NTRUdJU2FuZERhdGEvQ09WSUQtMTkvbWFzdGVyL2Nzc2VfY292aWRfMTlfZGF0YS9jc3NlX2NvdmlkXzE5X2RhaWx5X3JlcG9ydHMvMDUtMTItMjAyMC5jc3YnKSkKYGBgCgpgYGB7cn0Kc3RhdGVzX2RhdGEgPC0gam9obnNfaG9wa2luc19kYXRhICU+JQogIGZpbHRlcihDb3VudHJ5X1JlZ2lvbiA9PSAnVVMnKSAlPiUKICBzZWxlY3QoUHJvdmluY2VfU3RhdGUsIENvbmZpcm1lZCwgRGVhdGhzKSAlPiUKICBncm91cF9ieShQcm92aW5jZV9TdGF0ZSkgJT4lCiAgc3VtbWFyaXNlKHRvdGFsX2NvbmZpcm1lZCA9IHN1bShDb25maXJtZWQpLAogICAgICAgICAgICB0b3RhbF9kZWF0aHMgPSBzdW0oRGVhdGhzKSkgJT4lCiAgcmVuYW1lKHN0YXRlID0gUHJvdmluY2VfU3RhdGUpCgpzdGF0ZXNfZGF0YQpgYGAKClRoaXMgZGF0YSBpcyBpbnRlcmVzdGluZyBhcyBpcywgYnV0IGluIG9yZGVyIHRvIGJlIGFzIGhvbmVzdGx5IHJlcHJlc2VudGF0aXZlIGFzIHBvc3NpYmxlIG9mIHJlYWxpdHksIHdlIHJlYWxseSB3YW50IHRoaXMgZGF0YSB0byBiZSBzb21laG93IHJlbGF0aXZlIHRvIHRvdGFsIHBvcHVsYXRpb24gc2l6ZSBpbiBlYWNoIHN0YXRlLiBMZXQncyBpbXBvcnQgdGhlIG1vc3QgcmVjZW50IGRhdGEgZnJvbSB0aGUgVVMgQ2Vuc3VzIEJ1cmVhdSBhYm91dCB0aGUgcG9wdWxhdGlvbiBpbiBlYWNoIHN0YXRlIGFuZCBtZXJnZSBpdCB3aXRoIG91ciBvdGhlciBkYXRhLgpgYGB7cn0Kc3RhdGVfcG9wdWxhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKHJlYWRfY3N2KCdodHRwczovL3d3dzIuY2Vuc3VzLmdvdi9wcm9ncmFtcy1zdXJ2ZXlzL3BvcGVzdC9kYXRhc2V0cy8yMDEwLTIwMTkvc3RhdGUvZGV0YWlsL1NDUFJDLUVTVDIwMTktMTgrUE9QLVJFUy5jc3YnKSkKc3RhdGVfcG9wdWxhdGlvbiA8LSBzdGF0ZV9wb3B1bGF0aW9uICU+JQogIGZpbHRlcihOQU1FICE9ICdVbml0ZWQgU3RhdGVzJykgJT4lCiAgZmlsdGVyKE5BTUUgIT0gJ1B1ZXJ0byBSaWNvIENvbW1vbndlYWx0aCcpICU+JQogIHNlbGVjdChOQU1FLCBQT1BFU1RJTUFURTIwMTkpICU+JQogIHJlbmFtZShzdGF0ZSA9IE5BTUUsIHBvcHVsYXRpb24gPSBQT1BFU1RJTUFURTIwMTkpCnN0YXRlX3BvcHVsYXRpb24KYGBgCgpgYGB7cn0Kc3RhdGVzX2RhdGFfcGVyX3Rob3VzYW5kIDwtIGlubmVyX2pvaW4oc3RhdGVzX2RhdGEsIHN0YXRlX3BvcHVsYXRpb24sIGJ5PSdzdGF0ZScpICU+JQogIG11dGF0ZShjb25maXJtZWRfcGVyX3Rob3VzYW5kID0gMTAwMCAqIHRvdGFsX2NvbmZpcm1lZCAvIHBvcHVsYXRpb24sCiAgICAgICAgIGRlYXRoc19wZXJfdGhvdXNhbmQgPSAxMDAwICogdG90YWxfZGVhdGhzIC8gcG9wdWxhdGlvbikKc3RhdGVzX2RhdGFfcGVyX3Rob3VzYW5kCmBgYAoKQXdlc29tZSEgTm93IHdlIGhhdmUgZGF0YSByZWFkeSBmb3IgYm90aCB0aGUgc29jaWFsIGRpc3RhbmNpbm5nIGFzcGVjdCBvZiBlYWNoIHN0YXRlLCBhbmQgdGhlIGNhc2VzIGFuZCBkZWF0aHMgZm9yIGVhY2ggc3RhdGUuIEl0J3MgdGltZSB0byBwdXQgaXQgYWxsIHRvZ2V0aGVyIGFuZCB2aXN1YWxpemUgd2hhdCdzIGdvaW5nIG9uIGhlcmUuCgojIFRoZSBFZmZlY3RzIG9mIFNvY2lhbCBEaXN0YW5jaW5nIE1hbmRhdGVzIG9uIENvcm9uYXZpcnVzIENhc2VzIGFuZCBEZWF0aHMKCkZpcnN0LCBsZXQncyBtZXJnZSBvdXIgdHdvIGRhdHNldHMgc28gdGhhdCB3ZSBoYXZlIG9uZSBzb3VyY2UgdG8gcHVsbCBmcm9tIGZvciB0aGUgcmVzdCBvZiBvdXIgYW5hbHlzaXMgYW5kIHZpc3VhbGl6YXRpb24uIFdlIGtlZXAgb25seSB0aGUgY29sdW1ucyB3ZSBuZWVkLgpgYGB7cn0Kc2RfZGF0YSA8LSByb3duYW1lc190b19jb2x1bW4oY2x1c3RlcmluZ19kYXRhLCB2YXIgPSAnc3RhdGUnKQpjb21iaW5lZF9kYXRhIDwtIGlubmVyX2pvaW4oc2RfZGF0YSwgc3RhdGVzX2RhdGFfcGVyX3Rob3VzYW5kLCBieSA9ICdzdGF0ZScpICU+JQogIHNlbGVjdChzdGF0ZSwgYFNvY2lhbCBEaXN0YW5jaW5nIE1hbmRhdGVzYCwgY29uZmlybWVkX3Blcl90aG91c2FuZCwgZGVhdGhzX3Blcl90aG91c2FuZCkKCmNvbWJpbmVkX2RhdGEKYGBgCgoKYGBge3J9CmNvbWJpbmVkX2RhdGEgPC0gY29tYmluZWRfZGF0YSAgJT4lCiAgcmVuYW1lKGBEZWF0aHMgKHBlciB0aG91c2FuZClgID0gZGVhdGhzX3Blcl90aG91c2FuZCwKICAgICAgICAgYENhc2VzIChwZXIgdGhvdXNhbmQpYCA9IGNvbmZpcm1lZF9wZXJfdGhvdXNhbmQsCiAgICAgICAgIGBTb2NpYWwgRGlzdGFuY2luZ2AgPSBgU29jaWFsIERpc3RhbmNpbmcgTWFuZGF0ZXNgKSAlPiUKICBtdXRhdGUoYERlYXRocyAocGVyIHRob3VzYW5kKWAgPSByb3VuZChgRGVhdGhzIChwZXIgdGhvdXNhbmQpYCwzKSwKICAgICAgICAgYENhc2VzIChwZXIgdGhvdXNhbmQpYCA9IHJvdW5kKGBDYXNlcyAocGVyIHRob3VzYW5kKWAsIDMpLAogICAgICAgICB0ZXh0ID0gcGFzdGUoJ1N0YXRlOiAnLCBzdGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICdcbkNhc2VzIChwZXIgdGhvdXNhbmQpOiAnLCBgQ2FzZXMgKHBlciB0aG91c2FuZClgLAogICAgICAgICAgICAgICAgICAgICAgJ1xuRGVhdGhzIChwZXIgdGhvdXNhbmQpOiAnLCBgRGVhdGhzIChwZXIgdGhvdXNhbmQpYCkpCmBgYAoKYGBge3J9CmNvbWJpbmVkX2RhdGEKYGBgCgpXZSBzdGFydCBieSBwbG90dGluZyBkZWF0aHMgb3ZlciBjYXNlcywgZ3JvdXBlZCBieSBzdHJpY3RuZXNzIG9mIHNvY2lhbCBkaXN0YW5jaW5nIHBvbGljaWVzLiBUcnkgaG92ZXJpbmcgb3ZlciB0aGVzZSBkYXRhcG9pbnRzIC0tIHlvdSBjYW4gc2VlIHdoaWNoIHN0YXRlIGVhY2ggcG9pbnQgYmVsb25ncyB0bywgYW5kIHRoZSByZWxldmFudCBtZXRyaWNzLgoKYGBge3J9CnNjYXR0ZXIgPC0gZ2dwbG90KGNvbWJpbmVkX2RhdGEsIGFlcyh4PWBDYXNlcyAocGVyIHRob3VzYW5kKWAsIHk9YERlYXRocyAocGVyIHRob3VzYW5kKWAsICB0ZXh0PXRleHQpKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9YFNvY2lhbCBEaXN0YW5jaW5nYCkpIAoKZ2dzYXZlKCdTb2NpYWxEaXN0YW5jaW5nU2NhdHRlci5wbmcnKQppbnRlcmFjdGl2ZV9zY2F0dGVyIDwtIGdncGxvdGx5KHNjYXR0ZXIsIHRvb2x0aXAgPSAndGV4dCcpCmh0bWx3aWRnZXRzOjpzYXZlV2lkZ2V0KGludGVyYWN0aXZlX3NjYXR0ZXIsICdTb2NpYWxEaXN0YW5jaW5nU2NhdHRlcl9JbnRlcmFjdGl2ZS5odG1sJykKaW50ZXJhY3RpdmVfc2NhdHRlcgpgYGAKCkl0IGxvb2tzIGxpa2UgdGhlcmUgbWlnaHQgYmUgYSBkaWZmZXJlbmNlIGluIGRlYXRocyBwZXIgY2FzZXMgZm9yIGVhY2ggc29jaWFsIGRpc3RhbmNpbmcgZ3JvdXAsIGVzcGVjaWFsbHkgaW4gdGhlIGJvdHRvbSBsZWZ0IGNvcm5lciBvZiBvdXIgZ3JhcGgsIHdoZXJlIHdlIGhhdmUgYm90aCBncm91cHMgcGxvdHRlZCBvdmVyIGEgcmFuZ2Ugb2Ygc2ltaWxhciBjYXNlcyBwZXIgdGhvdXNhbmQgY291bnRzLiBMZXQncyB0aHJvdyBzb21lIHJlZ3Jlc3Npb24gbGluZXMgb250byBvdXIgIGRhdGEgdG8gc2VlIGlmIHRoaXMgaHlwb3RoZXNpcyBob2xkcyBhbnkgd2VpZ2h0LgoKYGBge3J9CmxpbmVzIDwtIGdncGxvdChjb21iaW5lZF9kYXRhLCBhZXMoeD1gQ2FzZXMgKHBlciB0aG91c2FuZClgLCB5PWBEZWF0aHMgKHBlciB0aG91c2FuZClgKSkgKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yPWBTb2NpYWwgRGlzdGFuY2luZ2ApKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLCBmb3JtdWxhID0geX54LCBzZT1GQUxTRSwgYWVzKGNvbG9yPWBTb2NpYWwgRGlzdGFuY2luZ2ApKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLCBmb3JtdWxhID0geX54LCBzZT1GQUxTRSwgY29sb3IgPSAnZGFya2dyYXknKQoKZ2dzYXZlKCdTb2NpYWxEaXN0YW5jaW5nUmVncmVzc2lvbi5wbmcnKQpnZ3Bsb3RseShsaW5lcykKYGBgCgpZaWtlcyEgSW4gdGhlIHBsb3QgYWJvdmUsIHRoZSBibHVlIGFuZCByZWQgbGluZXMgcmVwcmVzZW50IChyZXNwZWN0aXZlbHkpIHRoZSByZWdyZXNzaW9uIGxpbmVzIGFzc29jaWF0ZWQgd2l0aCB0aGUgZGF0YSBmb3IgbW9yZSBzdHJpY3QgYW5kIGxlc3Mgc3RyaWN0IHN0YXRlcy4gVGhlIGdyYXkgbGluZSBpcyB0aGUgcmVncmVzc2lvbiBmb3IgdGhlIGVudGlyZSBkYXRhc2V0LiBCdXQgdGhlIGdyYXkgbGluZSBtYXRjaGVzIGFsbW9zdCBleGFjdGx5IHdpdGggdGhlIGJsdWUgbGluZSEhIFdoeSBpcyB0aGlzPyBXZWxsLCBub3RpY2UgaG93IGZhciBvdXQgZnJvbSB0aGUgcmVzdCBvZiB0aGUgZGF0YSB0aGF0IHRoZSBzdGF0ZXMgYXJlIHdpdGggdGhlIHdvcnN0IGNvdW50cyBmb3IgY2FzZXMgYW5kIGRlYXRocy4gVGhlc2UgbWFzc2l2ZSBvdXRsaWVycyB3aWxsIGFic29sdXRlbHkgZG9taW5hdGluZyB0aGUgcmVncmVzc2lvbiwgd2hldGhlciB3ZSBhcmUgbG9va2luZyBhdCBhIHN1YnNldCBvZiB0aGUgZGF0YSBvciB0aGUgZW50aXJlIHRoaW5nLiBTaW5jZSBhbGwgb2YgdGhlIG91dGxpZXJzIGZhbGwgaW50byB0aGUgIk1vcmUgU3RyaWN0IiBjYXRlZ29yeSwgdGhpcyBjYXVzZXMgdGhlIGJsdWUgYW5kIGdyYXkgbGluZXMgdG8gYmUgdmVyeSBzaW1pbGFyLiAoQXMgYW4gYXNpZGUsIGl0IG1ha2VzIGNvbXBsZXRlIHNlbnNlIHRoYXQgYWxsIG9mIHRoZSBvdXRseWluZyBzdGF0ZXMgaGF2ZSBtb3JlIHN0cmljdCBzb2NpYWwgZGlzdGFuY2luZyBwb2xpY2llcy4gKk9mIGNvdXJzZSogdGhlIHN0YXRlcyBiZWluZyBpbXBhY3RlZCB0aGUgbW9zdCBzZXZlcmVseSB3b3VsZCBiZSB0YWtpbmcgdGhlIGdyZWF0ZXN0IG1lYXN1cmVzLikKCkxldCdzIHRha2UgYSBxdWljayBwZWVrIGF0IGhvdyBtYW55IG9mIHRoZXNlIHBvaW50cyBjYW4gYmUgY2xhc3NpZmllZCBhcyBvdXRsaWVycywgcmVtb3ZlIHRob3NlIGZyb20gb3VyIGRhdGFzZXQsIGFuZCBydW4gYSBuZXcgcmVncmVzc2lvbi4KCmBgYHtyfQpjYXNlc19ib3hwbG90IDwtIGJveHBsb3QoY29tYmluZWRfZGF0YSRgQ2FzZXMgKHBlciB0aG91c2FuZClgLCAKICAgICAgICBob3Jpem9udGFsID0gVFJVRSwKICAgICAgICB4bGFiID0gJ0Nhc2VzIChwZXIgdGhvdXNhbmQpJywKICAgICAgICBtYWluID0gJ0Rpc3RyaWJ1dGlvbiBvZiBDT1ZJRC0xOSBjYXNlcyBpbiB0aGUgNTAgc3RhdGVzJywKICAgICAgICBjb2wgPSAnI2MyODhlMycsCiAgICAgICAgYm9yZGVyID0gJyNhMThiYWQnKQoKZGVhdGhzX2JveHBsb3QgPC0gYm94cGxvdChjb21iaW5lZF9kYXRhJGBEZWF0aHMgKHBlciB0aG91c2FuZClgLCAKICAgICAgICBob3Jpem9udGFsID0gVFJVRSwKICAgICAgICB4bGFiID0gJ0RlYXRocyAocGVyIHRob3VzYW5kKScsCiAgICAgICAgbWFpbiA9ICdEaXN0cmlidXRpb24gb2YgQ09WSUQtMTkgZGVhdGhzIGluIHRoZSA1MCBzdGF0ZXMnLAogICAgICAgIGNvbCA9ICcjZmZlNTYzJywKICAgICAgICBib3JkZXIgPSAnI2IwYWM5YicpCmBgYAoKYGBge3J9CmNvbWJpbmVkX2RhdGEkY2FzZXNfcmFuayA8LSByYW5rKGNvbWJpbmVkX2RhdGEkYENhc2VzIChwZXIgdGhvdXNhbmQpYCkKY29tYmluZWRfZGF0YSRkZWF0aHNfcmFuayA8LSByYW5rKGNvbWJpbmVkX2RhdGEkYERlYXRocyAocGVyIHRob3VzYW5kKWApCm5vX291dGxpZXJzIDwtIGNvbWJpbmVkX2RhdGEgICU+JQogIGZpbHRlcihjYXNlc19yYW5rIDw9IDQ2KSAlPiUKICBmaWx0ZXIoZGVhdGhzX3JhbmsgPD0gNDUpCmBgYApgYGB7cn0KbGVzc19saW5lcyA8LSBnZ3Bsb3Qobm9fb3V0bGllcnMsIGFlcyh4PWBDYXNlcyAocGVyIHRob3VzYW5kKWAsIHk9YERlYXRocyAocGVyIHRob3VzYW5kKWApKSArCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9YFNvY2lhbCBEaXN0YW5jaW5nYCkpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sIGZvcm11bGEgPSB5fngsIHNlPVRSVUUsIGFlcyhjb2xvcj1gU29jaWFsIERpc3RhbmNpbmdgKSkKCmdnc2F2ZSgnU29jaWFsRGlzdGFuY2luZ1JlZ3Jlc3Npb25fTm9PdXRsaWVycy5wbmcnKQpsZXNzX2xpbmVzCmBgYAoKVGhpcyBpcyBpbnRlcmVzdGluZyEgSXQgKmRvZXMqIGFwcGVhciB0aGF0IHRoZXJlIG1heSBiZSBhIGRpZmZlcmVuY2UgaW4gc2xvcGVzIGJldHdlZW4gb3VyIGdyb3Vwcy4gV2UgaGVzaXRhdGUgdG8gbWFrZSBhbnkgYnJvYWQgY2xhaW1zIGFib3V0IHRoaXMgd2l0aG91dCBtb3JlIHJpZ29yb3VzIHN0YXRpc3RpY2FsIGFuYWx5c2lzLCBidXQgZm9yIGV4cGxvcmF0b3J5IHB1cnBvc2VzIGl0IGlzIGF0IGxlYXN0IHRob3VnaHQtcHJvdm9raW5nLiBFdmVuIGlmIGl0J3MgaW50ZXJlc3RpbmcgdGhvdWdoLCBpcyB0aGlzIHRoZSByaWdodCBxdWVzdGlvbiB0byBhc2s/IFRoZSBzbG9wZXMgaGVyZSB3b3VsZCByZXByZXNlbnQgKmRlYXRoIHJhdGVzKiBvZiBDT1ZJRC0xOSwgaW4gdGVybXMgb2YgcGVvcGxlIHdobyBoYXZlIGNvbnRyYWN0ZWQgdGhlIGRpc2Vhc2UuCgpbUHJlc2lkZW50IFRydW1wIHRoaW5rcyB0aGF0IHRoZXJlJ3MgYSBtb3JlIGltcG9ydGFudCBtZWFzdXJlbWVudCB0aGFuIGRlYXRoIHJhdGVzXShodHRwczovL2tobi5vcmcvbmV3cy90cnVtcHMtY29tcGFyaXNvbi1vZi1jb3ZpZC0xOS1kZWF0aC1yYXRlcy1pbi1nZXJtYW55LXVzLWlzLXdyb25nLyk6IG51bWJlciBvZiBkZWF0aHMgcmVsYXRpdmUgdG8gcG9wdWxhdGlvbiBzaXplLiBJdCdzIG5vdCB0b28gaGFyZCBmb3IgdXMgdG8gdmlzdWFsaXplIHRoYXQgbWV0cmljLgoKYGBge3J9CmJveGVzIDwtIGdncGxvdChjb21iaW5lZF9kYXRhLCBhZXMoeCA9IGBTb2NpYWwgRGlzdGFuY2luZ2AsIAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBgRGVhdGhzIChwZXIgdGhvdXNhbmQpYCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGBTb2NpYWwgRGlzdGFuY2luZ2ApKSArCiAgZ2VvbV9ib3hwbG90KGNvbG9yID0gJ2RhcmtncmF5JykgKwogIGdndGl0bGUoJ0NPVklELTE5IERlYXRocywgYnkgc3RyaWN0bmVzcyBvZiBzdGF0ZXdpZGUgc29jaWFsIGRpc3RhbmNpbmcgcG9saWNpZXMnKSArCiAgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMC41KSkKCmdnc2F2ZSgnU29jaWFsRGlzdGFuY2luZ0JveHBsb3RzLnBuZycpCmludGVyYWN0aXZlX2JveGVzIDwtIGdncGxvdGx5KGJveGVzKQpodG1sd2lkZ2V0czo6c2F2ZVdpZGdldChpbnRlcmFjdGl2ZV9ib3hlcywgJ1NvY2lhbERpc3RhbmNpbmdCb3hwbG90c19JbnRlcmFjdGl2ZS5odG1sJykKaW50ZXJhY3RpdmVfYm94ZXMKYGBgCgpBdCBhIGdsYW5jZSwgdGhlIG9ubHkgaW5zaWdodCB0byBiZSBnYXRoZXJlZCBmcm9tIHRoaXMgZ3JhcGggaXMgdGhhdCB0aGVyZSBpcyBtdWNoIG1vcmUgdmFyaWFuY2UgaW4gZGVhdGhzIHBlciB0aG91c2FuZCBhbW9uZyBzdGF0ZXMgd2l0aCBtb3JlIHN0cmljdCBzb2NpYWwgZGlzdGFuY2luZyBwb2xpY2llcy4gQnV0IGdvIGFoZWFkLCB0YWtlIGFkdmFudGFnZSBvZiB0aGUgaW50ZXJhY3Rpdml0eSBvZiB0aGlzIHBsb3QsIGFuZCB6b29tIGluIG9uIHRoZSBwYXJ0cyBvZiB0aGUgZ3JhcGggd2hlcmUgb3VyIGJveHBsb3RzIG92ZXJsYXAuIExvb2tpbmcgY2xvc2VseSwgd2Ugc2VlIHRoYXQgbGl0dGxlIChpZiBhbnkpIG9mIHRoZSBpbm5lciA1MCUgb2YgdGhlc2UgdHdvIGRhdGFzZXRzIGlzIG92ZXJsYXBwaW5nLgoKV2UgcGVyZm9ybSBhIHJvdWdoIChhZG1pdHRlZGx5IHNsb3BweSkgdGVzdCB0byBzZWUgaWYgdGhlcmUgaXMgYSBkaWZmZXJlbmNlIGluIG1lYW4gZGVhdGhzIHBlciB0aG91c2FuZCBiZXR3ZWVuIG91ciB0d28gZ3JvdXBzLiBXZSBzdGFydCB3aXRoIGEgaGlzdG9ncmFtIHRvIHZpc3VhbGl6ZSBub3JtYWxpdHkuCgpgYGB7cn0KZ2dwbG90KGNvbWJpbmVkX2RhdGEsIGFlcyh4ID0gYERlYXRocyAocGVyIHRob3VzYW5kKWAsIGZpbGwgPSBgU29jaWFsIERpc3RhbmNpbmdgKSkgKwogIGdlb21faGlzdG9ncmFtKGFscGhhID0gMC42LCBjb2xvciA9ICdncmF5JykKYGBgCkhtbW1tLi4uIEkgd291bGQgZGVmaW5pdGVseSB3YW50IHRvIGRvIG1vcmUgcmlnb3JvdXMgYW5hbHlzaXMgb24gdGhlIG5vcm1hbGl0eSBvZiB0aGlzIGRhdGEuIFBvdGVudGlhbGx5LCBpdCBpcyBhZGVxdWF0ZWx5IHNpbWlsYXIgdG8gYSBub3JtYWwgZGlzdHJpYnV0aW9uLCBidXQgKGF0IGxlYXN0KSB0aGUgTW9yZSBTdHJpY3Qgc3RhdGVzIGFyZSBkZWZpbml0ZWx5IHJpZ2h0LXNrZXdlZC4gV2UnbGwgZ28gYWhlYWQgd2l0aCB0aGUgdGVzdCwgYnV0IGtlZXAgaW4gbWluZCB0aGF0IG91ciBhc3N1bXB0aW9ucyBmb3IgdGhpcyB0ZXN0IGhhdmUgbm90IGJlZW5uIGZ1bGx5IGZsZXNoZWQgb3V0LgoKYGBge3J9Cmxlc3Nfc3RyaWN0X2RlYXRocyA8LSBjb21iaW5lZF9kYXRhICU+JQogIGZpbHRlcihgU29jaWFsIERpc3RhbmNpbmdgPT0nTGVzcyBTdHJpY3QnKSAlPiUKICBzZWxlY3QoYERlYXRocyAocGVyIHRob3VzYW5kKWApCgptb3JlX3N0cmljdF9kZWF0aHMgPC0gY29tYmluZWRfZGF0YSAlPiUKICBmaWx0ZXIoYFNvY2lhbCBEaXN0YW5jaW5nYD09J01vcmUgU3RyaWN0JykgJT4lCiAgc2VsZWN0KGBEZWF0aHMgKHBlciB0aG91c2FuZClgKQoKdC50ZXN0KGxlc3Nfc3RyaWN0X2RlYXRocywgbW9yZV9zdHJpY3RfZGVhdGhzLCBhbHRlcm5hdGl2ZSA9ICd0d28uc2lkZWQnLCB2YXIuZXF1YWwgPSBGQUxTRSkKYGBgCgpXb3d6YSwgdGhhdCdzIGEgdGlueSBwLXZhbHVlISBBdCBhbnkgcmVhc29uYWJsZSBsZXZlbCBvZiBzaWduaWZpY2FuY2UsIHRoaXMgc2hvd3MgdGhhdCB0aGUgZGF0YSBzdWdnZXN0cyBhIGRpZmZlcmVuY2UgaW4gbWVhbiBkZWF0aHMgcGVyIHRob3VzYW5kIGJldHdlZW4gdGhlIHR3byBncm91cHMuCg==
Social Distancing Data Sources
To begin our analysis of the effectiveness of social distancing, we look at a dataset which reports information about the measures mandated in each of the 50 U.S. States. KFF has a dataset with extensive records detailing this information. Let’s begin by loading in this dataset. It contains a lot of columns with a lot of information, but for our purposes we’re going to focus on Location, Stay at Home Order, Large Gatherings Ban, and Restaurant Limits. These 3 categories were selected over the others based on the magnitude of available data, variance between states, and relevance to enforcing social distancing. Please note that while this dataset was grabbed from KFF as linked, the csv was mildly massaged before loading into R (removing metadata lines, footnotes, ensuring consistent number of columns in each row). This data was downloaded on and accurate as of May 12, 2020.
Transforming Categorical Variables
Our goal is to use k-means clustering analysis to identify classifications of how strict each state is with their social distancing policies. However, our data in it’s current state contains only categorical variables, and k-means clustering requires the use of numeric variables. Luckily, the categories we have are inherently ordinal, so let’s define a mapping for each variable to become “numerical”. We will take the categories for each variable, and assign them an integer value between 0 and 10, loosely defined as 0 being the “least strict” and 10 being the “most strict”. Keeping all 3 predictor variables on a similar scale allows k-means to effectively analyze clusters without allowing any single variable to overrun the analysis.
As a note, this type of transformation is highly subjective: more of an art form than a science. A different analyst may have different ideas about how to assign weight to the levels of each factor. Regardless, perhaps the most important principles to follow during this process is to keep each variable on scales of similar magnitude.
Transforming “Stay at Home Order”
We can see here that there are 4 categories of responses to the “Stay at Home” variable: Lifted, High-Risk Groups, Rolled Back to High Risk Groups, and Statewide. There is also a “-” response, indicating some type of missing value. Which states only record “-” for Stay at Home Order?
Some outside research indicates that the value is missing because these states actually have no stay at home order! This must be the meaning of “-”. Turns out then, we actually have 5 categories for this variable.
This is where it becomes subjective, but we will do our best to create a fair mapping between this somewhat-ordinal data and a numerical 0-10 scale. Below we assign each category of “Stay at Home Order” to a numerical value, and create a new column to record these.
Transforming “Large Gatherings Ban”
We will follow similar steps to transform the “Large Gatherings Ban” predictor variable.
Most of these categories seem to make sense and are fairly ordinal. However, two of the categories are unclear in their meaning: ‘-’ and ‘Other’. Let’s look at which states fall into these categories and see if we can learn more information.
Doing some outside research, it appears Minnesota has banned large gatherings for high school graduation ceremonies, but has made no general mandates for large gatherings outside of that, only suggestions. It appears North Dakota has not made a mandate concerning large gatherings.
Research indicates that Connecticut is banning gatherings with >5 people, Florida prohibits gatherings of 10+ on beaches only, and Rhode Island bans gatherings of 10+.
Based on these findings for MN, ND, CT, FL, and RI, we define new categories of “None”, “Special types of gatherings prohibited”, and “>5 People Prohibited”. We will then re-classify the “Large Gatherings Ban” attribute in ways that make sense for these 5 states.
Let’s look at the unique levels of this factor one more time to make sure nothing went awry.
Great! Now let’s go ahead and assign numerical scores to each of these levels, creating a new column in our dataframe to store those values. Notice that ‘Expanded to >25 People Prohibited’ and ‘Expanded to 25+ People Prohibited’ are two different labels, but we will treat these as equally strict categories.
Transforming “Restaurant Limits”
Finally, we transform the “Restaurant Limits” predictor variable.
South Dakota is the only state without a clearly defined entry for Restaurant Limits, but since there is no statewide ban on restaurants in South Dakota, we will just assign the “-” value a very low score when we create the numeric columns.